package dex

import (
	"errors"
	"io"
	"io/ioutil"
)

// File Dex文件结构
type File struct {
	raw       []byte
	stream    Reader
	link      SizeOff // 静态链接文件中使用的数据
	mapOff    int     // map_list的偏移
	stringIds SizeOff // 字符串标识符列表
	typeIds   SizeOff // 类型标识符列表
	protoIds  SizeOff // 方法原型标识符列表
	fieldIds  SizeOff // 字段标识符列表
	methodIds SizeOff // 方法标识符列表
	classDefs SizeOff // 类定义列表
	data      SizeOff // map_list 数据区

	dexClasses []Class  //　存放field、method数据
	Strings    []string // 存放字符串表的数据
}

// NewFile 从APK直接读取Dex到内容，从内存中初始化File
func NewFile(reader io.Reader) (File, error) {
	dexFile := File{
		dexClasses: make([]Class, 0),
	}

	dexBytes, _ := ioutil.ReadAll(reader)
	// dexFile.raw, _ = ioutil.ReadAll(reader)
	dexFile.raw = dexBytes
	dexFile.stream = Reader{
		data: dexFile.raw,
		pos:  0,
	}
	// dexFile.seek(0)

	// 跳过
	dexFile.stream.pos = 0x20
	dexBytesLen := len(dexBytes)
	if dexBytesLen != int(dexFile.stream.U32()) {
		return dexFile, errors.New("Dex长度异常！")
	}

	if dexFile.stream.U32() != 0x70 {
		return dexFile, errors.New("Dex头长度异常！")
	}

	if dexFile.stream.U32() != 0x12345678 {
		return dexFile, errors.New("Dex endian_tag异常！")
	}

	dexFile.link = dexFile.stream.readSizeOff()
	dexFile.mapOff = int(dexFile.stream.U32())
	dexFile.stringIds = dexFile.stream.readSizeOff()
	dexFile.typeIds = dexFile.stream.readSizeOff()
	dexFile.protoIds = dexFile.stream.readSizeOff()
	dexFile.fieldIds = dexFile.stream.readSizeOff()
	dexFile.methodIds = dexFile.stream.readSizeOff()
	dexFile.classDefs = dexFile.stream.readSizeOff()
	dexFile.data = dexFile.stream.readSizeOff()

	dexFile.Strings = make([]string, dexFile.stringIds.size)

	// classes 对象数组

	return dexFile, nil
}

// ParseStrings 解析字符串表
func (d *File) ParseStrings() {
	for i := 0; i < d.stringIds.size; i++ {
		s := d.String(i)
		d.Strings = append(d.Strings, s)
	}
}

// Stream 从Dex文件中，读一段数据 （TODO 这种情况是否会增加内存？）
func (d *File) Stream(offset int) Reader {
	return Reader{
		data: d.raw,
		pos:  offset,
	}
}

func (d *File) String(index int) string {
	s1 := d.Stream(d.stringIds.off + index*4)
	dataOff := s1.U32()

	s2 := d.Stream(int(dataOff))
	cstrOff := s2.Uleb128()

	return s2.ReadCStr(int(cstrOff))
}

// Type 获取类名/类型
func (d *File) Type(index int) string {
	offset := d.typeIds.off + index*4
	stream := d.Stream(offset)
	strIdx := stream.U32()
	return d.String(int(strIdx))
}

func (d *File) parseClassDefs() {
	for i := 0; i < d.classDefs.size; i++ {
		dexClass := NewClass(d, d.classDefs.off, i)
		dexClass.parseData()
		d.dexClasses = append(d.dexClasses, dexClass)
	}
}

// Class 相关
type Class struct {
	dexFile           *File // 指针才对，而不是传值
	className         string
	supperClassName   string
	access            int
	dataOff           int
	data              ClassData
	constantValuesOff int
}

// NewClass 实例化Class对象
func NewClass(dexFile *File, offset int, idx int) Class {
	dexClass := Class{}
	dexClass.dexFile = dexFile

	stream := dexFile.Stream(offset + idx*32)
	dexClass.className = dexFile.Type(int(stream.U32()))
	dexClass.access = int(stream.U32())
	dexClass.supperClassName = dexFile.Type(int(stream.U32()))

	// skip interface list
	stream.U32()
	// interfaceOff := stream.U32()
	// println("interfaceOff", interfaceOff)
	// if interfaceOff != 0 {
	// 	istream := dexFile.Stream(interfaceOff)
	// 	size := istream.U32()
	// 	for i := 0; i < size; i++ {
	// 		println("interface", dexFile.Type(int(istream.U16())))
	// 	}
	// }

	stream.U32()
	stream.U32()

	dexClass.dataOff = int(stream.U32())
	dexClass.constantValuesOff = int(stream.U32())

	return dexClass
}

func (d *Class) parseData() {
	d.data = NewClassData(d.dexFile, d.dataOff)
	// TODO 解析field数据。暂时没必要。
	// 	fmt.Print(d.constantValuesOff)
	// 	if d.constantValuesOff > 0 {
	// 		stream := d.dexFile.Stream(d.constantValuesOff)
	// 		fmt.Println(stream) // 解析洗后的数据
	// 	}
}

// Field Field结构
type Field struct {
	dex           *File
	id            FieldID
	access        int
	constantValue string
}

// NewField New Field
func NewField(dex *File, fieldIdx int, access int) Field {
	f := Field{}
	f.dex = dex
	f.id = NewFieldID(dex, fieldIdx)
	f.access = access
	f.constantValue = ""

	return f
}

// FieldID FieldID结构
type FieldID struct {
	className string
	desc      string
	name      string
}

// NewFieldID 新建FieldID对象
func NewFieldID(dex *File, fieldIdx int) FieldID {
	fid := FieldID{}
	stream := dex.Stream(dex.fieldIds.off + fieldIdx*8)
	fid.className = dex.Type(int(stream.U16()))
	fid.desc = dex.Type(int(stream.U16()))
	fid.name = dex.String(int(stream.U32()))

	return fid
}

// Method 方法结构
type Method struct {
	dex     *File
	id      MethodID
	access  int
	codeOff int
	code    CodeItem
}

// NewMethod new method obj
func NewMethod(dex *File, methodIdx int, acess int, codeOff int) Method {
	m := Method{}
	m.dex = dex
	m.id = NewMethodID(dex, methodIdx)
	m.access = acess
	m.codeOff = codeOff

	return m
}

// MethodID MethodID结构
type MethodID struct {
	className  string
	name       string
	returnType string
	paramTypes string
	sign       string
	desc       string
}

// NewMethodID new method id
func NewMethodID(dex *File, methodIdx int) MethodID {
	mi := MethodID{}
	stream := dex.Stream(dex.methodIds.off + methodIdx*8)
	mi.className = dex.Type(int(stream.U16()))
	protoIdx := int(stream.U16())
	mi.name = dex.String(int(stream.U32()))

	// fmt.Println(mi.className, mi.name)

	stream2 := dex.Stream(dex.protoIds.off + protoIdx*12)
	// shortyIdx := stream2.U32() #skip
	stream2.pos += 4
	returnIdx := stream2.U32()
	parametersOff := int(stream2.U32())
	mi.returnType = dex.Type(int(returnIdx))
	mi.paramTypes = TypeList(dex, parametersOff)
	mi.sign = "(" + mi.paramTypes + ")" + mi.returnType
	mi.desc = mi.className + "->" + mi.name + mi.sign
	return mi
}

// TypeList 获取参数列表
func TypeList(dex *File, offset int) string {
	p := ""
	if offset == 0 {
		return p
	}
	stream := dex.Stream(offset)
	size := int(stream.U32())

	for i := 0; i < size; i++ {
		p += dex.Type(int(stream.U16()))
	}

	return p
}

func (mid *MethodID) getSpacedParamTypes(isstatic int) {

}

// CodeItem 代码结构
type CodeItem struct {
	nregs int // register_size

	insnsSize int
	tries     int
	listOff   int
	bytecode  int // TODO
}

// NewCodeItem New CodeItem
func NewCodeItem(dex *File, offset int) CodeItem {
	codeItem := CodeItem{}
	// stream := dex.Stream(offset)
	// codeItem.nregs = stream.U16()
	// insSize := stream.U16()
	// outsSize := stream.U16()
	// triesSize := stream.U16()
	// debugOff := stream.U32()
	// codeItem.insnsSize = stream.U32()
	// insnsStartPos := stream.pos
	// TODO ...

	return codeItem
}

// ClassData Class 中的数据，包含字段、方法。
type ClassData struct {
	fileds  []Field
	methods []Method
}

// NewClassData 初始化ClassData
func NewClassData(dexFile *File, offset int) ClassData {
	classData := ClassData{}

	// 如果为0，表示没有fields、methods
	if offset != 0 {
		classData.parse(dexFile, dexFile.Stream(offset))
	}

	return classData
}

func (c *ClassData) parse(dexFile *File, stream Reader) {
	numstatic := int(stream.Uleb128())
	numinstance := int(stream.Uleb128())
	numdirect := int(stream.Uleb128())
	numvirtual := int(stream.Uleb128())

	for _, i := range []int{numstatic, numinstance} {
		fieldIdx := 0
		for j := 0; j < i; j++ {
			fieldIdx += int(stream.Uleb128())
			ac := int(stream.Uleb128())
			f := NewField(dexFile, fieldIdx, ac)
			c.fileds = append(c.fileds, f)
		}
	}

	for _, i := range []int{numdirect, numvirtual} {
		mtdIdx := 0
		for j := 0; j < i; j++ {
			mtdIdx += int(stream.Uleb128())
			access := int(stream.Uleb128())
			codeOff := int(stream.Uleb128())
			m := NewMethod(dexFile, mtdIdx, access, codeOff)
			c.methods = append(c.methods, m)
		}
	}
}
